#include "nservercore.h"

#include <QProcess>
#include <time.h>

NServerCore::NServerCore(QString configPath, QObject* parent) : QObject(parent) ///QTcpServer(parent)
{
    //инициализация интерфейсов
    db = new IDatabaseImpl;
    algExec = new IAlgorithmExecuteImpl;
    algAdding = new IAlgorithmAddImpl;
    userManager = new IUserManagerImpl;

    tcpSrvHttp = new QTcpServer(this);
    connect(tcpSrvHttp, SIGNAL(newConnection()), this, SLOT(httpIncomingConnection()));
    netProtocol = new NNetProtocol;

    //QtServiceBase::instance()->logMessage(tr("Loaging config: ") + configPath);
    QSettings setts(configPath, QSettings::IniFormat);

    settsHttpPort = setts.value("http/port", 80).toUInt();

    settsCompiler = setts.value("alg/compiler", "mingw32-gcc").toString().trimmed();
    settsBaseDir = setts.value("alg/base_dir", "./alg_base/").toString().trimmed();

    /*settsDBPort = setts.value("db/port", 5432).toUInt();
    settsDBHost = setts.value("db/host", "127.0.0.1").toString().trimmed();
    settsDBUser = setts.value("db/user", "test").toString().trimmed();
    settsDBPass = setts.value("db/password", "test").toString().trimmed();
    settsDBName = setts.value("db/dbname", "db_test").toString().trimmed();
    qDebug("[DB] host: %s, user: %s, pass %s, alias %s", qPrintable(settsDBHost), qPrintable(settsDBUser), qPrintable(settsDBPass), qPrintable(settsDBName));
    */
    QString dbPath = setts.value("db/path", "./db_unn").toString().trimmed();


    if (!tcpSrvHttp->listen(QHostAddress::Any, settsHttpPort))  //
    {
        qDebug("Can't listen http socket!");
        //QtServiceBase::instance()->logMessage("Can't listen http sock!", QtServiceBase::Error);
        //exit(1);
    }

    if (!(db->doInit(dbPath)))
    {
        qDebug("Error open DB!");
        return;
    }

    if (db->tables().contains(tr("users")) && db->tables().contains(tr("groups")) && db->tables().contains(tr("algs")))
    {
        qDebug("DB loading success");
    }
    QSqlQuery q("");
    if (!db->tables().contains(tr("groups")))
    {
        qDebug("Creating table groups");
        if (!db->exec("create table groups (id integer primary key autoincrement, name varchar)").isActive())
            qDebug("Table +groups+ creation failed!");
        db->exec("insert into groups values(NULL, \"admin\")");
        db->exec("insert into groups values(NULL, \"user\")");
    }
    if (!db->tables().contains(tr("users")))
    {
        qDebug("Creating table users");
        if (!db->exec("create table users (id integer primary key autoincrement, login varchar, pass varchar, group_id integer)").isActive())
            qDebug("Table +users+ creation failed!");
        db->exec("insert into users values(NULL, \"test\", \"test\", 2)");
        db->exec("insert into users values(NULL, \"test2\", \"test2\", 1)");
        }
    if (!db->tables().contains(tr("algs")))
    {
        qDebug("Creating table algs");
        if (!db->exec("create table algs (id integer primary key autoincrement, name varchar, desc varchar, owner_id integer, source text, exec_path varchar, can_read_groups varchar, can_write_groups varchar)").isActive())
            qDebug("Table +algs+ creation failed!");
    }

    //QSqlQuery q(db);
    //uint dtSecs = QDateTime::currentDateTime().toTime_t();
    //if (!q.exec(tr("insert into log(sensor_id, new_state, event_date_time) values(%1, %2, %3);").arg(1).arg(1).arg(1)))
    //    qDebug("Sensor state adding to table log failed!, err: %s", qPrintable(db->lastError().text()));


    //q = db->exec(tr("select id,login from users"));
    //q = db->exec(tr("select type,login from users where type in (select terminal from tickets where id in (select ticketId from tickets_games where gameId=\"%1\"));").arg(26));
    //int cnt=0;
    //if (q.isSelect())
    //    while (q.next()) qDebug("%d -> |%s| ++ |%s|", cnt++, qPrintable(q.value(0).toString()), qPrintable(q.value(1).toString()));
    //exit(0);
}

void NServerCore::httpIncomingConnection()
{
    NIncomeConnect conn;
    while (tcpSrvHttp->hasPendingConnections())
    {
        conn.sock = tcpSrvHttp->nextPendingConnection();
        connect(conn.sock, SIGNAL(readyRead()), this, SLOT(httpReadClient()));
        connect(conn.sock, SIGNAL(disconnected()), this, SLOT(httpDiscardClient()));

        connHttp.push_back(conn);
        //qDebug("New http income connection");
    }
    //QtServiceBase::instance()->logMessage("New Connection http");
}

void NServerCore::pause()
{
    ////disabled = true;
}

void NServerCore::resume()
{
    ///disabled = false;
}

NServerCore::~NServerCore()
{
    //освобождение интерфейсов
    delete db;
    delete algExec;

    delete algAdding;
    delete userManager;

    //очистка памяти
    delete netProtocol;
}

void NServerCore::httpProcessPacket(int connId, uint len, bool isGetReq, QByteArray requestUrlAll, QByteArray content, QList<QByteArray> cookies)
{
    //qDebug("HTTP request from %d (isGet=%d, req=%s), len=%d (real len=%d)", connId, isGetReq, requestUrl.data(), len, content.length());
    //qDebug("Msg: |%s|", content.data());

    QByteArray requestUrl = requestUrlAll.split('?').first().trimmed();
    QList<QByteArray> requestParams = requestUrlAll.split('?').last().trimmed().split('&');
    QByteArray contentAnswer;
    QString cookiesToSet;

    //for (int i=0; i < cookies.count(); i++)
        //qDebug("cookie come %d: |%s|", i, cookies[i].data());

    int userId=-1;  //userId < 0 - no auth, > 0 -> userId in table users:)
    if (!cookies.empty())       //check auth
    {
        for (int i=0; i < cookies.count(); i++)
            if (cookies.at(i).startsWith("check="))
            {
                //qDebug("Data as userId = |%s|", cookies.at(i).trimmed().mid(6, cookies.at(i).trimmed().count() - 32 - 6).data());
                userId = cookies.at(i).mid(6, cookies.at(i).count() - 32 - 6).toInt();
                if (authDataCache[userId] != cookies.at(i).right(32))
                {
                    //qDebug("CLEAR userId was: %d", userId);

                    userId = -1;
                    cookiesToSet = tr("check=");
                }
                break;
            }
    }

    if (requestUrl.endsWith("/"))
        requestUrl.append("index.htm");
    if (requestUrl.contains("/shadow/"))    //some templates:)))
        requestUrl = "404.htm";


    if ((requestUrl == "/index.htm") && (!isGetReq))
    {                       //check auth now           //POST login+pass

        contentAnswer = getWWWFile("/index.htm");

        QByteArray login, pass;
        QList<QByteArray> params = content.split('&');
        for (int i=0; i < params.count(); i++)
        {
            if (params.at(i).startsWith("l="))
                login = params.at(i).split('=').last().trimmed();
            else if (params.at(i).startsWith("p="))
                pass = params.at(i).split('=').last().trimmed();
        }

        if (login == QByteArray(login).replace("\"", "").replace("\'", "").replace("\\", "").replace("/", "").replace(" ", "") &&
                pass == QByteArray(pass).replace("\"", "").replace("\'", "").replace("\\", "").replace("/", "").replace(" ", ""))
        {

            QSqlQuery q("");
            q = db->exec(tr("select login, pass, group_id, id from users where login='%1';").arg(QString::fromLatin1(login)));
            ///qDebug("select, %d", q.size());   //работает))

            while (q.next())
            {
                //qDebug("%d -> |%s| ++ |%s|", 0, qPrintable(q.value(0).toString()), qPrintable(q.value(1).toString()));

                //if ((login == "test") && (pass == "test"))
                if (q.value(1).toString() == pass)
                {
                    //int pageType = q.value(2).toInt();
                    //int settsAccess = q.value(3).toInt();

                    //now redirect:
                    //contentAnswer = getWWWFile(tr("/shadow/index_%1.htm").arg(sensorCount).toLocal8Bit());
                    //contentAnswer = contentAnswer.replace("%PAGE_TYPE%", QByteArray::number(pageType));

                    //cookiesToSet = tr("login=%1\r\ncheck=%2").arg(q.value(0).toString()).arg(QString(QCryptographicHash::hash(q.value(1).toString().toLocal8Bit(), QCryptographicHash::Md5).toHex()));

                    int usId = q.value(3).toInt();
                    authDataCache[usId] = db->generateAuthKey(usId, pass);
                    authDataGroup[usId] = q.value(2).toInt();
                    cookiesToSet = tr("check=%1%2").arg(usId).arg(QString(authDataCache[usId]));

                    httpSendRedirect(connHttp[connId].sock, tr("/"), cookiesToSet);
                    return;

                    //break;
                }
            }
        }

        if (!contentAnswer.isNull())
        {
            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }
    }
    else if ((requestUrl == "/quit"))
    {                       //clear auth

        if (userId > 0)
        {
            authDataCache[userId].clear();
            authDataGroup[userId] = 0;
        }
        cookiesToSet = tr("check=%1%2").arg("").arg("");

        httpSendRedirect(connHttp[connId].sock, tr("/"), cookiesToSet);
        return;
    }
    else if ((requestUrl == "/index.htm") && (isGetReq))
    {
        contentAnswer = getWWWFile("/index.htm");

        if (userId > 0)
        {
            contentAnswer = getWWWFile(tr("/shadow/index.htm").toLocal8Bit());

            QSqlQuery q2("");
            int algCount = 0;
            q2 = db->exec(tr("select count(*) from algs;"));
            if (q2.next())
                algCount = q2.value(0).toInt();

            QString groupName;
            q2 = db->exec(tr("select name from groups where id in (select group_id from users where id=%1);").arg(userId));
            if (q2.next())
                groupName = q2.value(0).toString();


            contentAnswer = contentAnswer.replace("%ALGCOUNT%", tr("%1").arg(algCount).toUtf8());
            contentAnswer = contentAnswer.replace("%ADMIN_LINK%", (groupName == tr("admin")) ? tr("<br><a href=\"./admin.htm\">Админка</a><br><br>").toUtf8() : "");
        }

        if (!contentAnswer.isNull())
        {
            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }
    }
    else if (requestUrl == "/admin.htm")
    {                       //settings page or settings apply

        contentAnswer = getWWWFile("/shadow/admin.htm");

        QSqlQuery q2("");
        QString groupName;
        if (userId > 0)
        {
            q2 = db->exec(tr("select name from groups where id in (select group_id from users where id=%1);").arg(userId));
            if (q2.next())
                groupName = q2.value(0).toString();
        }

        if ((userId > 0) && (groupName == tr("admin")))
        {
            QString groupsList = tr("<br><br>Список групп:<br><table width=\"40%\" border=\"1\">");
            QString groupsChangeLink;
            q2 = db->exec(tr("select id, name from groups;"));
            while (q2.next())
            {
                groupsList += tr("<tr><td>%1</td><td>%2</td></tr>").arg(q2.value(0).toInt()).arg(q2.value(1).toString());
                groupsChangeLink += tr("<a href=\"#\" onClick=\"changeGroup(|USER_ID|, %1);\">%2</a> &nbsp; ").arg(q2.value(0).toInt()).arg(q2.value(1).toString());
            }
            groupsList += tr("</table>");

            QString usersList = tr("Список пользователей:<br><table width=\"80%\" border=\"1\">");
            q2 = db->exec(tr("select users.id as usId, login, group_id, name from users, groups where (group_id=groups.id) or (group_id=0);"));
            while (q2.next())
                usersList += tr("<tr><td>%1</td><td>%2</td><td>%3</td><td>Изменить группу на: %4</td></tr>").arg(q2.value(0).toInt()).arg(q2.value(1).toString()).arg(q2.value(3).toString()).arg(QString(groupsChangeLink).replace("|USER_ID|", QString::number(q2.value(0).toInt())));
            usersList += tr("</table>");

            contentAnswer = contentAnswer.replace("%USER_LIST%", usersList.toUtf8());
            contentAnswer = contentAnswer.replace("%GROUPS_LIST%", groupsList.toUtf8());

            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }
    }
    else if ((requestUrl == "/group_add") && (!isGetReq))
    {                       //ajax group add from settings
        //qDebug("data come: |%s|", content.data());

        if (userId < 0)
            contentAnswer = "Ошибка: нет авторизации!";
        else
        {
            QByteArray groupName;
            QList<QByteArray> params = content.split('&');
            for (int i=0; i < params.count(); i++)
            {
                if (params.at(i).startsWith("group_name="))
                    groupName = QByteArray::fromPercentEncoding(params.at(i).split('=').last().trimmed());
            }
            groupName = QByteArray(groupName).replace("\"", "").replace("\'", "").replace("\\", "").replace("/", "").replace(" ", "");

            if (groupName.isEmpty())
                contentAnswer = "Ошибка: не заполнена форма!";
            else
            {
                QSqlQuery q("");
                q = db->exec(tr("select id from groups where name=\'%1\';").arg(QString::fromUtf8(groupName)));
                int cnt = 0;
                while (q.next())
                    cnt ++;

                if (cnt != 0)
                    contentAnswer = "Ошибка: группа существует!";
                else
                {
                    //add:)
                    if (!db->exec(tr("insert into groups values(NULL, \'%1\');").arg(QString::fromUtf8(groupName))).isActive())
                    {
                        qDebug("Group adding to table group failed!, err: %s", qPrintable(db->lastError().text()));
                        contentAnswer = "Ошибка добавления!";
                    }
                    else
                        contentAnswer = "Успешно добавлена группа: " + groupName;
                }
            }
        }

        httpSendAnswer(connHttp[connId].sock, 200, "plain", contentAnswer, cookiesToSet);
        return;
    }
    else if ((requestUrl == "/user_add") && (!isGetReq))
    {                       //ajax user add from settings
        //qDebug("data come: |%s|", content.data());

        if (userId < 0)
            contentAnswer = "Ошибка: нет авторизации!";
        else
        {
            QByteArray userLogin, userPass;
            QList<QByteArray> params = content.split('&');
            for (int i=0; i < params.count(); i++)
            {
                if (params.at(i).startsWith("user_login="))
                    userLogin = QByteArray::fromPercentEncoding(params.at(i).split('=').last().trimmed());
                else if (params.at(i).startsWith("user_pass="))
                    userPass = QByteArray::fromPercentEncoding(params.at(i).split('=').last().trimmed());
            }

            contentAnswer = userManager->addUser(db, userLogin, userPass);
        }

        httpSendAnswer(connHttp[connId].sock, 200, "plain", contentAnswer, cookiesToSet);
        return;
    }
    else if ((requestUrl == "/user_change") && (!isGetReq))
    {                       //ajax user change pass from settings
        //qDebug("data come: |%s|", content.data());

        if (userId < 0)
            contentAnswer = "Ошибка: нет авторизации!";
        else
        {
            QByteArray userLogin, userPass;
            QList<QByteArray> params = content.split('&');
            for (int i=0; i < params.count(); i++)
            {
                if (params.at(i).startsWith("user_login="))
                    userLogin = QByteArray::fromPercentEncoding(params.at(i).split('=').last().trimmed());
                else if (params.at(i).startsWith("user_pass="))
                    userPass = QByteArray::fromPercentEncoding(params.at(i).split('=').last().trimmed());
            }
            userLogin = QByteArray(userLogin).replace("\"", "").replace("\'", "").replace("\\", "").replace("/", "").replace(" ", "");
            userPass = QByteArray(userPass).replace("\"", "").replace("\'", "").replace("\\", "").replace("/", "").replace(" ", "");

            if ((userLogin.isEmpty()) || (userPass.isEmpty()))
                contentAnswer = "Ошибка: не заполнена форма!";
            else
            {

                QSqlQuery q("");
                q = db->exec(tr("select id from users where login=\'%1\';").arg(QString::fromUtf8(userLogin)));
                int cnt = 0, userPassId=0;
                while (q.next())
                {
                    userPassId = q.value(0).toInt();
                    cnt ++;
                }

                if (cnt != 1)
                    contentAnswer = "Ошибка: пользователь не существует!";
                else
                {
                    //modify:)
                    if (!db->exec(tr("update users set pass=\'%1\' where id=%2;").arg(QString::fromUtf8(userPass)).arg(userPassId)).isActive())
                    {
                        qDebug("User modify failed!, err: %s", qPrintable(db->lastError().text()));
                        contentAnswer = "Ошибка изменения!";
                    }
                    else
                        contentAnswer = "Пароль успешно изменен для пользователя " + userLogin;
                }
            }
        }

        httpSendAnswer(connHttp[connId].sock, 200, "plain", contentAnswer, cookiesToSet);
        return;
    }
    else if ((requestUrl == "/group_change") && (!isGetReq))
    {                       //ajax group change  from settings
        //qDebug("data come: |%s|", content.data());

        if (userId < 0)
            contentAnswer = "Ошибка: нет авторизации!";
        else
        {
            int curUserId=0, groupId=0;
            QList<QByteArray> params = content.split('&');
            for (int i=0; i < params.count(); i++)
            {
                if (params.at(i).startsWith("user_id="))
                    curUserId = params.at(i).split('=').last().toInt();
                else if (params.at(i).startsWith("group_id="))
                    groupId =  params.at(i).split('=').last().toInt();
            }

            contentAnswer = userManager->changeUserGroup(db, curUserId, groupId);
        }

        httpSendAnswer(connHttp[connId].sock, 200, "plain", contentAnswer, cookiesToSet);
        return;
    }
    else if ((requestUrl == "/run.htm") )
    {
        contentAnswer = getWWWFile(tr("/shadow/run.htm").toLocal8Bit());

        if (userId > 0)
        {
            QSqlQuery q2("");
            QString errString, resultString;
            QByteArray algName, algDesc;
            int algId = -1;

            for (int i=0; i < requestParams.count(); i++)
            {
                if (requestParams.at(i).startsWith("algid="))
                    algId = requestParams.at(i).split('=').last().trimmed().toInt();
            }

            if (algId <= 0)
            {
                contentAnswer = getWWWFile(tr("/shadow/run_error.htm").toLocal8Bit());
                httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
                return;
            }

            int groupId = -1;
            q2 = db->exec(tr("select group_id from users where id=%1;").arg(userId));
            if (q2.next())
                groupId = q2.value(0).toInt();
            q2 = db->exec(tr("select name, desc, id from algs where (can_read_groups like '%|%1|%' or owner_id=%2) and (id=%3);").arg(groupId).arg(userId).arg(algId));
            while (q2.next())
            {
                algName = q2.value(0).toString().toUtf8();
                algDesc = q2.value(1).toString().toUtf8();
            }
            if (algName.isEmpty() || algDesc.isEmpty())
            {
                contentAnswer = getWWWFile(tr("/shadow/run_error.htm").toLocal8Bit());
                httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
                return;
            }

            if (!isGetReq)
            {
                int arrInt[10];
                float arrFloat[10];
                for (int i=0; i < 10; i++)
                {
                    arrInt[i] = 0;
                    arrFloat[i] = 0.0f;
                }
                //qDebug("POST msg: |%s|", content.data());
                QList<QByteArray> params = content.split('&');
                int maxI=-1, maxF=-1;
                for (int i=0; i < params.count(); i++)
                {
                    if (params.at(i).startsWith("int"))
                    {
                        int num = params.at(i).split('=').first().split('_').last().toInt();
                        arrInt[num] = params.at(i).split('=').last().toInt();
                        if (num > maxI)
                            maxI = num;
                    }
                    else if (params.at(i).startsWith("float"))
                    {
                        int num = params.at(i).split('=').first().split('_').last().toInt();
                        arrFloat[num] = params.at(i).split('=').last().toFloat();
                        if (num > maxF)
                            maxF = num;
                    }
                }
                //for (int i=0; i < 10; i++)
                //    qDebug() << i << " - " << arrInt[i] << " - " << arrFloat[i];
                QString algArgs = tr("%1 %2 ").arg(maxI + 1).arg(maxF + 1);
                for (int i=0; i <= maxI; i++)
                    algArgs.append(tr("%1 ").arg(arrInt[i]));
                for (int i=0; i <= maxF; i++)
                    algArgs.append(tr("%1 ").arg(arrFloat[i]));


                if (QFile::exists(settsBaseDir+tr("alg_%1.exe").arg(algId)))
                {

                    QString resAlg = algExec->run(settsBaseDir+tr("alg_%1.exe %2").arg(algId), algArgs);

                    resultString = tr("Результат работы:<br>") + resAlg.replace("\n", "\n<br>");
                }
                else
                    errString = tr("Ошибка выполнения! Отсутствует исполяемый файл на сервере!");
            }

            QString allGroups;
            q2 = db->exec(tr("select id, name from groups;"));
            while (q2.next())
            {
                allGroups += tr("<tr><td>%1</td><td><input name=\"chb_read_%2\" type=\"checkbox\"></td><td><input name=\"chb_write_%2\" type=\"checkbox\"></td></tr>").arg(q2.value(1).toString()).arg(q2.value(0).toInt());
            }
            contentAnswer = contentAnswer.replace("%ERROR%", errString.toUtf8());
            contentAnswer = contentAnswer.replace("%RESULT%", resultString.toUtf8());
            contentAnswer = contentAnswer.replace("%ALG_ID%", QString::number(algId).toUtf8());
            contentAnswer = contentAnswer.replace("%ALG_NAME%", algName);
            contentAnswer = contentAnswer.replace("%ALG_DESC%", QString::fromUtf8(algDesc).replace("\n", "\n<br>").toUtf8());

            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }
    }
    else if ((requestUrl == "/upload.htm") )
    {
        contentAnswer = getWWWFile(tr("/shadow/upload.htm").toLocal8Bit());

        if (userId > 0)
        {
            QSqlQuery q2("");
            QString errString;
            QByteArray algName, algDesc, algCode;
            QString algCodeTemplate = tr("//вставьте свои функции здесь\n//...\n\nvoid start(int* argI, float* argF)\n{\n   //вставьте свой код здесь\n\n\n   int res=10;\n   printf(\"result: %d\", res);\n}\n");
            if (!isGetReq)
            {
                //qDebug("POST msg: |%s|", content.data());
                QString algGroupRead=tr("|"), algGroupWrite=tr("|");
                QList<QByteArray> params = content.split('&');
                for (int i=0; i < params.count(); i++)
                {
                    if (params.at(i).startsWith("name="))
                        algName = QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').toUtf8();
                    else if (params.at(i).startsWith("desc="))
                        algDesc = QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').trimmed().toUtf8();
                    else if (params.at(i).startsWith("code="))
                        algCode = QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').trimmed().toUtf8();
                    else if (params.at(i).startsWith("chb_read_"))
                    {
                        //QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').toUtf8();
                        int groupId = params.at(i).split('=').first().split('_').last().toInt();
                        algGroupRead += QString::number(groupId) + tr("|");
                    }
                    else if (params.at(i).startsWith("chb_write_"))
                    {
                        //QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').toUtf8();
                        int groupId = params.at(i).split('=').first().split('_').last().toInt();
                        algGroupWrite += QString::number(groupId) + tr("|");
                    }
                }

                if ((!algName.isEmpty()) && (!algCode.isEmpty()) && (!algDesc.isEmpty()) && (!algCode.contains("#include")))
                {                     //compiling alg!

                    QString binAlgName = tr("temp_%1.exe").arg(1+clock());
                    QString codeAlgName = QString(binAlgName).replace(".exe", ".cpp");
                    QByteArray result = algAdding->addNewAlgorithm(settsBaseDir, settsCompiler, algCode, binAlgName, codeAlgName);

                    if (!result.isEmpty())//(result.contains("error") || result.contains("Error") || result.contains("ERROR"))
                        errString = tr("Ошибка сборки! Текст ошибки: <br>%1").arg(QString(result).replace("C:\\WINDOWS\\TEMP\\", "some_build_path\\").replace("\n", "\n<br>"));
                    else
                    {
                        q2 = db->exec(tr("insert into algs values (NULL, '%1', '%2', %3, '%4', '%5', '%6', '%7')").arg(QString::fromUtf8(algName)).arg(QString::fromUtf8(algDesc)).arg(userId).arg(QString::fromUtf8(algCode)).arg("").arg(algGroupRead).arg(algGroupWrite));
                        if (!q2.isActive())
                        {
                            qDebug() << "\n\n"<< q2.lastError().text() << "\n\n";
                            //qDebug() << "query: |" << tr("insert into algs values (NULL, \"%1\", \"%2\", %3, \"%4\", \"%5\", \"%6\", \"%7\")").arg(QString::fromUtf8(algName)).arg(QString::fromUtf8(algDesc)).arg(userId).arg(QString::fromUtf8(algCode).replace("\"", "'")).arg("").arg(algGroupRead).arg(algGroupWrite) << "|\n";
                            qDebug("Trouble with alg adding");
                            errString = tr("Ошибка внесения алгоритма в базу данных!");
                        }
                        else
                        {
                            //rename binary!
                            int algId = q2.lastInsertId().toInt();
                            QFile(settsBaseDir + binAlgName).rename(settsBaseDir + tr("alg_%1.exe").arg(algId));

                            //qDebug("POST code: |%s|", algCode.data());
                            contentAnswer = getWWWFile(tr("/shadow/upload_ok.htm").toLocal8Bit());
                        }
                    }
                }
                else if (algCode.contains("#include"))
                    errString = tr("Код содержит недопустимые символы!");
                else
                    errString = tr("Не заполнены основные поля формы!");

                if (!errString.isEmpty())
                    errString = tr("<br><span style=\"color: red; font: bold;\">") + errString + tr("</span><br><br>");
            }


            QString allGroups;
            q2 = db->exec(tr("select id, name from groups;"));
            while (q2.next())
            {
                allGroups += tr("<tr><td>%1</td><td><input name=\"chb_read_%2\" type=\"checkbox\"></td><td><input name=\"chb_write_%2\" type=\"checkbox\"></td></tr>").arg(q2.value(1).toString()).arg(q2.value(0).toInt());
            }
            contentAnswer = contentAnswer.replace("%ERROR%", errString.toUtf8());
            contentAnswer = contentAnswer.replace("%GROUP_RIGHTS%", allGroups.toUtf8());
            contentAnswer = contentAnswer.replace("%ALG_NAME%", algName.isEmpty() ? tr("алгоритм %1").arg(clock()%1000).toUtf8() : algName);
            contentAnswer = contentAnswer.replace("%ALG_DESC%", algDesc.isEmpty() ? tr("принимаемые параметры:\nint[0]-...").toUtf8() : algDesc);
            contentAnswer = contentAnswer.replace("%ALG_CODE%", algCode.isEmpty() ? algCodeTemplate.toUtf8() : algCode);

            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }
    }
    else if ((requestUrl == "/edit.htm") )
    {
        contentAnswer = getWWWFile(tr("/shadow/edit.htm").toLocal8Bit());

        if (userId > 0)
        {
            QSqlQuery q2("");
            QString errString;
            QByteArray algName, algDesc, algCode;
            QString canWriteGroups, canReadGroups;

            int algId = -1;

            for (int i=0; i < requestParams.count(); i++)
            {
                if (requestParams.at(i).startsWith("algid="))
                    algId = requestParams.at(i).split('=').last().trimmed().toInt();
            }

            if (algId <= 0)
            {
                contentAnswer = getWWWFile(tr("/shadow/edit_error.htm").toLocal8Bit());
                httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
                return;
            }

            int groupId = -1;
            q2 = db->exec(tr("select group_id from users where id=%1;").arg(userId));
            if (q2.next())
                groupId = q2.value(0).toInt();
            q2 = db->exec(tr("select name, desc, source, can_read_groups, can_write_groups from algs where (can_write_groups like '%|%1|%' or owner_id=%2) and (id=%3);").arg(groupId).arg(userId).arg(algId));
            while (q2.next())
            {
                algName = q2.value(0).toString().toUtf8();
                algDesc = q2.value(1).toString().toUtf8();
                algCode = q2.value(2).toString().toUtf8();

                canReadGroups = q2.value(3).toString();
                canWriteGroups = q2.value(4).toString();
            }
            if (algName.isEmpty())
            {
                contentAnswer = getWWWFile(tr("/shadow/edit_error.htm").toLocal8Bit());
                httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
                return;
            }

            if (!isGetReq)
            {
                //qDebug("POST msg: |%s|", content.data());
                QString algGroupRead=tr("|"), algGroupWrite=tr("|");
                QList<QByteArray> params = content.split('&');
                for (int i=0; i < params.count(); i++)
                {
                    if (params.at(i).startsWith("name="))
                        algName = QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').toUtf8();
                    else if (params.at(i).startsWith("desc="))
                        algDesc = QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').trimmed().toUtf8();
                    else if (params.at(i).startsWith("code="))
                        algCode = QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').trimmed().toUtf8();
                    else if (params.at(i).startsWith("chb_read_"))
                    {
                        //QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').toUtf8();
                        int groupId = params.at(i).split('=').first().split('_').last().toInt();
                        algGroupRead += QString::number(groupId) + tr("|");
                    }
                    else if (params.at(i).startsWith("chb_write_"))
                    {
                        //QUrl::fromPercentEncoding(params.at(i).split('=').last().trimmed()).replace('+', ' ').toUtf8();
                        int groupId = params.at(i).split('=').first().split('_').last().toInt();
                        algGroupWrite += QString::number(groupId) + tr("|");
                    }
                }

                if ((!algName.isEmpty()) && (!algCode.isEmpty()) && (!algDesc.isEmpty()) && (!algCode.contains("#include")))
                {                     //compiling alg!

                    QString binAlgName = tr("temp_%1.exe").arg(1+clock());
                    QString codeAlgName = QString(binAlgName).replace(".exe", ".cpp");
                    QFile fileTmpl(settsBaseDir + tr("template.cpp"));
                    fileTmpl.open(QIODevice::ReadOnly);
                    QByteArray algBaseCode = fileTmpl.readAll();
                    fileTmpl.close();

                    algBaseCode = algBaseCode.replace("%ALG_CODE%", algCode);
                    QFile fileTmpl2(settsBaseDir+codeAlgName);
                    fileTmpl2.open(QIODevice::WriteOnly);
                    fileTmpl2.write(algBaseCode);
                    fileTmpl2.close();

                    QProcess process;
                    process.start(settsCompiler + tr(" ") + settsBaseDir + codeAlgName + tr(" -o ") + settsBaseDir + binAlgName);
                    process.waitForFinished();

                    //qDebug("cmd: |%s|", (settsCompiler + tr(" ") + settsBaseDir + tr("template.cpp -o ") + settsBaseDir + binAlgName).toLocal8Bit().data());
                    //qDebug("workdir: |%s|", process.workingDirectory().toLocal8Bit().data());

                    QByteArray result = process.readAllStandardError();//.readAllStandardOutput();
                    //qDebug("build res: |%s|", result.data());

                    QFile::remove(settsBaseDir+codeAlgName);

                    if (!result.isEmpty())//(result.contains("error") || result.contains("Error") || result.contains("ERROR"))
                        errString = tr("Ошибка сборки! Текст ошибки: <br>%1").arg(QString(result).replace("C:\\WINDOWS\\TEMP\\", "some_build_path\\").replace("\n", "\n<br>"));
                    else
                    {
                        if (!db->exec(tr("update algs set name='%1', desc='%2', source='%3', can_read_groups='%4', can_write_groups='%5' where id=%6 ;").arg(QString::fromUtf8(algName)).arg(QString::fromUtf8(algDesc)).arg(QString::fromUtf8(algCode)).arg(algGroupRead).arg(algGroupWrite).arg(algId)).isActive())
                        {
                            qDebug() << "\n\n"<< q2.lastError().text() << "\n\n";
                            //qDebug() << "query: |" << tr("insert into algs values (NULL, \"%1\", \"%2\", %3, \"%4\", \"%5\", \"%6\", \"%7\")").arg(QString::fromUtf8(algName)).arg(QString::fromUtf8(algDesc)).arg(userId).arg(QString::fromUtf8(algCode).replace("\"", "'")).arg("").arg(algGroupRead).arg(algGroupWrite) << "|\n";
                            qDebug("Trouble with alg editing");
                            errString = tr("Ошибка обновления алгоритма в базе данных!");
                        }
                        else
                        {
                            //rename binary!
                            int algId = q2.lastInsertId().toInt();
                            QFile(settsBaseDir + binAlgName).rename(settsBaseDir + tr("alg_%1.exe").arg(algId));

                            //qDebug("POST code: |%s|", algCode.data());
                            contentAnswer = getWWWFile(tr("/shadow/edit_ok.htm").toLocal8Bit());
                        }
                    }
                }
                else if (algCode.contains("#include"))
                    errString = tr("Код содержит недопустимые символы!");
                else
                    errString = tr("Не заполнены основные поля формы!");

                if (!errString.isEmpty())
                    errString = tr("<br><span style=\"color: red; font: bold;\">") + errString + tr("</span><br><br>");
            }


            QString allGroups;
            q2 = db->exec(tr("select id, name from groups;"));
            while (q2.next())
            {
                allGroups += tr("<tr><td>%1</td><td><input name=\"chb_read_%2\" type=\"checkbox\" ").arg(q2.value(1).toString()).arg(q2.value(0).toInt()) + (canReadGroups.indexOf(tr("|%1|").arg(q2.value(0).toInt())) != -1 ? tr("checked=\"checked\"") : tr("")) + tr(" ></td><td><input name=\"chb_write_%1\" type=\"checkbox\" ").arg(q2.value(0).toInt()) + (canWriteGroups.indexOf(tr("|%1|").arg(q2.value(0).toInt())) != -1 ? tr("checked=\"checked\" ></td></tr>") : tr("></td></tr>"));
            }
            contentAnswer = contentAnswer.replace("%ERROR%", errString.toUtf8());
            contentAnswer = contentAnswer.replace("%ALG_ID%", QString::number(algId).toUtf8());
            contentAnswer = contentAnswer.replace("%GROUP_RIGHTS%", allGroups.toUtf8());
            contentAnswer = contentAnswer.replace("%ALG_NAME%", algName.isEmpty() ? tr("алгоритм %1").arg(clock()%1000).toUtf8() : algName);
            contentAnswer = contentAnswer.replace("%ALG_DESC%", algDesc.isEmpty() ? tr("принимаемые параметры:\nint[0]-...").toUtf8() : algDesc);
            contentAnswer = contentAnswer.replace("%ALG_CODE%", algCode.isEmpty() ? tr("").toUtf8() : algCode);

            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }
    }
    else if ((requestUrl == "/list.htm") )
    {
        contentAnswer = getWWWFile(tr("/shadow/list.htm").toLocal8Bit());

        if (userId > 0)
        {
            QSqlQuery q2("");
            QSqlQuery q3("");
            QString errString;

            int groupId = -1;
            q2 = db->exec(tr("select group_id from users where id=%1;").arg(userId));
            if (q2.next())
                groupId = q2.value(0).toInt();
            else
                errString = tr("Пользователь не найден!");

            //qDebug("%d", groupId);
            QString allAlgs;
            q2 = db->exec(tr("select name, owner_id, can_write_groups, id from algs where can_read_groups like '%|%1|%' or owner_id=%2;").arg(groupId).arg(userId));
            while (q2.next())
            {
                QString ownerName = "";
                q3 = db->exec(tr("select login from users where id=%1;").arg(q2.value(1).toInt()));
                if (q3.next())
                    ownerName = q3.value(0).toString();
                else
                    ownerName = tr("Владелец не найден!");

                allAlgs += tr("<tr><td>%1</td><td>%2</td><td><a href=\"./run.htm?algid=%3\">Выполнить</a>").arg(q2.value(0).toString()).arg(ownerName).arg(q2.value(3).toInt()) + (q2.value(2).toString().indexOf(tr("|%1|").arg(groupId)) != -1 || q2.value(1).toInt() == userId ? tr("<br /><a href=\"./edit.htm?algid=%1\">Редактировать</a><br /><a href=\"./del.htm?algid=%1\">Удалить</a>").arg(q2.value(3).toInt()) : tr("")) + tr("</td></tr>");
            }
            contentAnswer = contentAnswer.replace("%ERROR%", errString.toUtf8());
            contentAnswer = contentAnswer.replace("%LIST_ALGS%", allAlgs.toUtf8());

            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }
    }
    else        //not a special page:)
    {

        contentAnswer = getWWWFile(requestUrl);
        if (!contentAnswer.isNull())
        {
            httpSendAnswer(connHttp[connId].sock, 200, QFileInfo(requestUrl).suffix().toLower().toLocal8Bit(), contentAnswer, cookiesToSet);
            return;
        }

    }

    QByteArray content404 = getWWWFile(QByteArray("/404.htm"));
    if (!content404.isNull())
        httpSendAnswer(connHttp[connId].sock, 404, "html", content404, cookiesToSet);
    else
        httpSendAnswer(connHttp[connId].sock, 404, "html", "<b>Page not found, sorry!</b>", cookiesToSet);
}

QByteArray NServerCore::getWWWFile(QByteArray path)     //if no file, returns NULL qbytearray!
{
    if (QFile::exists(tr("./www") + path))
    //if (QFile::exists(tr("/home/projects/unn_web_service_src/www") + path))
    {
        QFile fileCon(tr("./www") + path);
        //QFile fileCon(tr("/home/projects/unn_web_service_src/www") + path);          //DEBUG: from local:))))
        if (!fileCon.open(QIODevice::ReadOnly))
        {
            qDebug("Some trouble with opening file /www%s", path.data());
            return QByteArray();    //IMPORTANT!! NULL!!!
        }
        QByteArray content = fileCon.readAll();//(100000);
        //qDebug("size: %d", content.size());
        return content;
    }
    else
        qDebug("sended 404: file doesn't exist!");

    return QByteArray();    //IMPORTANT!! NULL!!!
}

void NServerCore::httpReadClient()
{
    QTcpSocket* sock = (QTcpSocket*)sender();

    int curConnId = -1;
    for (int i=0; i < connHttp.count(); i++)
        if (connHttp[i].sock == sock)
        {
            curConnId = i;
            break;
        }
    if (curConnId < 0)
    {
        qDebug("Unknown incoming HTTP connection has data to read! (%d bytes, |%s|)", sock->bytesAvailable(), sock->readAll().data());
        sock->abort();
        return;
    }

    if (sock->bytesAvailable() == 0)
    {   //closed!
        qDebug("empty receiving -> HTTP connetion closed!");
        sock->abort();
        return;
    }

    uchar* sockBuf = connHttp[curConnId].sockBuf;
    uint &sockBufCnt = connHttp[curConnId].sockBufCnt;
    //if (sock->bytesAvailable() > 0)
    //    connIncome[curConnId].lastReceivedMsgTime = clock();       //current:)

    while (sock->bytesAvailable() > 0)
    {
        sock->read((char*)& sockBuf[sockBufCnt], 1);
        sockBuf[sockBufCnt+1] = 0;

        QByteArray curStr((char*)sockBuf, sockBufCnt+1);    //with 0-ended

        //qDebug("MSG: |%s|", sockBuf);

        if (netProtocol->checkDataForMessage(curStr))
        {
            if ((curStr.startsWith("GET ")) || (curStr.startsWith("POST ")))
            {
                QList<QByteArray> cookiesParsed, msgBegin;
                msgBegin = netProtocol->getHeaders();
                for (int i=0; i < msgBegin.count(); i++)
                    if (msgBegin[i].startsWith("Cookie: "))     //only once should be:)
                    {
                        cookiesParsed = msgBegin[i].trimmed().mid(8).replace(" ", "").split(';');
                        //break;
                    }

                httpProcessPacket(curConnId, netProtocol->getDataLength(), curStr.startsWith("GET "), netProtocol->getReqUrl(), netProtocol->getMsgBody(), cookiesParsed);

                memset(sockBuf, 0, 65500);
                sockBufCnt = 0;
                continue;
            }
            else
                qWarning("BAD BEGINNING OF PACKET! preambula=%c %c %c %c)",  sockBuf[0], sockBuf[1], sockBuf[2], sockBuf[3]);
        }

        sockBufCnt ++;
        if (sockBufCnt > SOCK_BUFFER_SIZE)
        {
            sock->readAll();
            sock->abort();
            sock->deleteLater();
            qDebug("Close HTTP sock cause msg was very long!");
            connHttp.remove(curConnId);
            return;
        }
    }
}

void NServerCore::timerEvent(QTimerEvent* e)
{
}

void NServerCore::httpSendRedirect(QTcpSocket *sock, QString url, QString cookies)
{
    QByteArray msg;
    msg.append("HTTP/1.0 303 See Other\r\n");
    msg.append("Server: NexServer v2.0\r\n");

    QStringList cookLst = cookies.split("\r\n");
    for (int i=0; i < cookLst.count(); i++)
        if (!cookLst.at(i).isEmpty())
            msg.append("Set-Cookie: " + cookLst.at(i).toLocal8Bit()+ "\r\n");

    msg.append(tr("Location: %1\r\n").arg(url).toLocal8Bit());
    msg.append("\r\n");

    sock->write(msg);
    sock->close();
}

void NServerCore::httpSendAnswer(QTcpSocket *sock, uint respCode, QByteArray contentExt, QByteArray data, QString cookies)
{
    if (data.length() == 0)
    {
        qDebug("Can't send! Msg is empty!");
        return;
    }

    QByteArray msg;
    if (respCode == 200)
        msg.append("HTTP/1.0 200 OK\r\n");
    else if (respCode == 500)       //server error:)
        msg.append("HTTP/1.0 500 Server Error\r\n");
    else
        msg.append("HTTP/1.0 404 File not found\r\n");

    msg.append("Server: NexServer v2.0\r\n");

    QStringList cookLst = cookies.split("\r\n");
    for (int i=0; i < cookLst.count(); i++)
        if (!cookLst.at(i).isEmpty())
            msg.append("Set-Cookie: " + cookLst.at(i).toLocal8Bit()+ "\r\n");

    if ((contentExt == "html") ||  (contentExt == "htm"))
        msg.append("Content-type: text/html\r\n");
    else if (contentExt == "js")
        msg.append("Content-type: application/x-javascript\r\n");
    else if (contentExt == "gif")
        msg.append("Content-type: image/gif\r\n");
    else if (contentExt == "png")
        msg.append("Content-type: image/png\r\n");
    else if ((contentExt == "jpeg") || (contentExt == "jpg"))
        msg.append("Content-type: image/jpeg\r\n");
    else if (contentExt == "bmp")
        msg.append("Content-type: image/bmp\r\n");
    else if (contentExt == "exe")
        msg.append("Content-type: application/octet-stream\r\n");
    else if (contentExt == "css")
        msg.append("Content-type: text/css\r\n");
    else if (contentExt == "swf")
        msg.append("Content-type: application/x-shockwave-flash\r\n");
    else if (contentExt == "xml")
        msg.append("Content-type: text/xml\r\n");
    else
        msg.append("Content-type: text/plain\r\n");

    msg.append(tr("Content-length: %1\r\n").arg(data.length()).toLocal8Bit());
    msg.append("\r\n");
    msg.append(data);

    sock->write(msg);

    //qDebug("http answer sended, data len=%d", data.length());
}

void NServerCore::httpDiscardClient()
{
    QTcpSocket* sock = (QTcpSocket*)sender();

    int curConnId = -1;
    for (int i=0; i < connHttp.count(); i++)
        if (connHttp[i].sock == sock)
        {
            curConnId = i;
            break;
        }

    sock->deleteLater();
    if (curConnId < 0)
    {
        qDebug("Unknown incoming HTTP connection closed!");
        sock->deleteLater();
        return;
    }

    //qDebug("Incoming connection HTTP sock closed!");
    connHttp.remove(curConnId);
}
